﻿using System;
using System.ComponentModel;

namespace MvvmFx.Common.ViewModels.Behaviors.Validation
{
    public partial class ValidationService : IDataErrorInfo
    {
        private readonly ViewModelBase _viewModel;
        private ValidationRules _validationRules;

        #region [ Constructor ]

        /// <summary>
        /// Initializes a new instance of the <see cref="MvvmFx.Common.ViewModels.Behaviors.Validation.ValidationService"/> class.
        /// </summary>
        public ValidationService(ViewModelBase viewModel)
        {
            _viewModel = viewModel;

            InitializeBusinessRules();

            // Track the property changed event of the viewModel, 
            // and signal in the ChangeService when it's modified.
            _viewModel.PropertyChanged += ViewModelPropertyChanged;
        }

        #endregion

        private void InitializeBusinessRules()
        {
            AddInstanceBusinessRules();
            if (!(SharedValidationRules.RulesExistFor(this.GetType())))
            {
                lock (this.GetType())
                {
                    if (!(SharedValidationRules.RulesExistFor(this.GetType())))
                    {
                        SharedValidationRules.GetManager(this.GetType(), true);
                        AddBusinessRules();
                    }
                }
            }
        }
        
        /// <summary>
        /// Provides access to the broken rules functionality.
        /// </summary>
        /// <remarks>
        /// This property is used within your business logic so you can
        /// easily call the AddRule() method to associate validation
        /// rules with your object's properties.
        /// </remarks>
        public ValidationRules ValidationRules
        {
            get
            {
                if (_validationRules == null)
                {
                    _validationRules = new ValidationRules(_viewModel);
                }
                else if (_validationRules.Target == null)
                    _validationRules.SetTarget(_viewModel);
                return _validationRules;
            }
        }

        /// <summary>
        /// Override this method in your business class to
        /// be notified when you need to set up business
        /// rules.
        /// </summary>
        /// <remarks>
        /// This method is automatically called by CSLA .NET
        /// when your object should associate per-instance
        /// validation rules with its properties.
        /// </remarks>
        protected virtual void AddInstanceBusinessRules()
        {

        }

        /// <summary>
        /// Override this method in your business class to
        /// be notified when you need to set up shared 
        /// business rules.
        /// </summary>
        /// <remarks>
        /// This method is automatically called by CSLA .NET
        /// when your object should associate per-type 
        /// validation rules with its properties.
        /// </remarks>
        protected virtual void AddBusinessRules()
        {

        }

        /// <summary>
        /// Returns <see langword="true" /> if the object 
        /// and its child objects are currently valid, 
        /// <see langword="false" /> if the
        /// object or any of its child objects have broken 
        /// rules or are otherwise invalid.
        /// </summary>
        /// <remarks>
        /// <para>
        /// By default this property relies on the underling ValidationRules
        /// object to track whether any business rules are currently broken for this object.
        /// </para><para>
        /// You can override this property to provide more sophisticated
        /// implementations of the behavior. For instance, you should always override
        /// this method if your object has child objects, since the validity of this object
        /// is affected by the validity of all child objects.
        /// </para>
        /// </remarks>
        /// <returns>A value indicating if the object is currently valid.</returns>
        public virtual bool IsValid
        {
            // Check if children are valid (if set to track children)
            get { return IsSelfValid; } //&& (_fieldManager == null || FieldManager.IsValid()); }
        }

        /// <summary>
        /// Returns <see langword="true" /> if the object is currently 
        /// valid, <see langword="false" /> if the
        /// object has broken rules or is otherwise invalid.
        /// </summary>
        /// <remarks>
        /// <para>
        /// By default this property relies on the underling ValidationRules
        /// object to track whether any business rules are currently broken for this object.
        /// </para><para>
        /// You can override this property to provide more sophisticated
        /// implementations of the behavior. 
        /// </para>
        /// </remarks>
        /// <returns>A value indicating if the object is currently valid.</returns>
        public virtual bool IsSelfValid
        {
            get { return ValidationRules.IsValid; }
        }

        /// <summary>
        /// Provides access to the readonly collection of broken business rules
        /// for this object.
        /// </summary>
        /// <returns>A Csla.Validation.RulesCollection object.</returns>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public virtual BrokenRulesCollection BrokenRulesCollection
        {
            get { return ValidationRules.GetBrokenRules(); }
        }

        #region [ IDataErrorInfo ]

        public string Error
        {
            get
            {
                if (!IsSelfValid)
                    return ValidationRules.GetBrokenRules().ToString(
                        RuleSeverity.Error);
                else
                    return String.Empty;
            }
        }

        public string this[string columnName]
        {
            get
            {
                string result = string.Empty;
                if (!IsSelfValid)
                {
                    BrokenRule rule =
                        ValidationRules.GetBrokenRules().GetFirstBrokenRule(columnName);
                    if (rule != null)
                        result = rule.Description;
                }
                return result;
            }
        }

        #endregion
    }
}